#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
#include <iostream>
#include <string>
using namespace std;

#define QUEUESIZE 5

/* This method convert a string to title-case by capitalizing the first *
 * character of every word and converting the rest of the word to lower *
 * case.                                                                *
 * - sentence is the char array of the string                           *
 * - start_index is the index of the array to start convertion          *
 * - size is the index of the array to end convertion                   */

void titlize(char *sentence, const unsigned int start_index, const unsigned int size)
{
	char ch;
    
    /*counter is used to skip whitespaces*/
	int counter = 0;
    
    /*convert the string from start_index to size*/
	for(unsigned int i = start_index; i < size; i++){
		ch = sentence[i];
        
        /*first char of a word*/
		if(isalpha(ch) && counter == 0)
			sentence[i] = toupper(ch);
        
        /*rest char of a word*/
		else if(isalpha(ch))
			sentence[i] = tolower(ch);
        
        /*whitespace*/
		else if(isspace(ch)){
			sentence[i] = ch;
			counter = 0;
			continue;
		}
        
		counter++;
	}
}

/*This method is used to create a socket, bind it, and listen on it*/

int establish()
{
    int socket_fd;
    struct sockaddr_in server;
    
    /*clear out address*/
    memset(&server, 0, sizeof(sockaddr_in));
    
    /*set up the host addr type, port number and ip address of server*/
    server.sin_family = AF_INET;
    server.sin_port = 0;
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    
    /*create a socket*/
    socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if( socket_fd < 0 ){
		cerr << "fail to create socket" << endl;
    	exit(-1);
	}
    
    /*rebind multi-connections*/
    int yes = 1;
    if((setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int))) < 0 ){
        cerr << "fail to set socket option" << endl;
        exit(-1);
    }
    
    /*bind the socket*/
    if( bind(socket_fd, (struct sockaddr*)&server, sizeof(struct sockaddr)) < 0 ){
        close(socket_fd);
		cerr << "fail to bind the socket" << endl;
        exit(-1);
    }
    
    /*listen on the socket*/
    if( listen(socket_fd, QUEUESIZE) < 0 ){
		cerr << "fail to listen on the socket" << endl;
        exit(-1);
    }
    
    /*print server address*/
    char hostname[256];
    gethostname(hostname, 256);
    cout << "SERVER_ADDRESS " << hostname << endl;
    
    /*print port number*/
    socklen_t server_size = sizeof(server);
    getsockname(socket_fd, (struct sockaddr *)&server, &server_size);
    cout << "SERVER_PORT " << ntohs(server.sin_port) << endl;
    
    return socket_fd;
}

/*main method*/
int main(){
    
    /*set up socket*/
	int socket_fd = establish();
    
    struct sockaddr_in client;
    socklen_t client_size;
	int result;
    int new_fd;
	fd_set readset;
    int fds[QUEUESIZE];
    char buffer[1024];
    
    /*accept connection from client, and receive or send msg to client*/
	for(;;) {
        
        /*select the client*/
        FD_ZERO(&readset);
        FD_SET(socket_fd, &readset);
		for(int i = 0; i < QUEUESIZE; i++){
            if(fds[i] > 0) FD_SET(fds[i], &readset);
        }
        
        result = select(FD_SETSIZE, &readset, NULL, NULL, NULL);
        if(result < 0){
            cerr << "fail to select readable sockets" << endl;
            exit(-1);
        }
        if(result == 0){
            cerr << "cilent has exited" << endl;
            
        }
        
        /*accept the connection from client*/
        if(FD_ISSET(socket_fd, &readset) ){
            client_size = sizeof(client);
            new_fd = accept(socket_fd,(struct sockaddr*)&client,&client_size);
            if( new_fd < 0 ){
                cerr << "cilent closed the socket" << endl;
                exit(-1);
            }
            
            if( new_fd == 0 ){
                cerr << "cilent closed the socket" << endl;
                exit(-1);
            }
            
            int j;
            for(j = 0; j < QUEUESIZE; j++){
                if(fds[j] == 0){
                    fds[j] = new_fd;
                    break;
                }
            }
            if( j == QUEUESIZE )
                close(new_fd);
		}
        
        /*provide service to client */
        for(int i=0; i<QUEUESIZE; i++){
            if(FD_ISSET(fds[i], &readset)){
                
                /*clean up buffer*/
                memset(buffer,0,sizeof(buffer));
                
                /*receive the string from client*/
                int size = recv(fds[i],buffer,sizeof(buffer),0);
                
                /*get the size of the string*/
                int temp_size = (buffer[0]<<24)|(buffer[0]<<16)|(buffer[0]<<8)|(buffer[3]);
                
                /*print the string*/
                for(int j=4;j<temp_size+4;j++){
                    cout << buffer[j];
                }
                cout << endl;
                
                /*call titlize to convert the string*/
                titlize(buffer, 4, size);
                
                /*send the converted string back to client*/
                send(fds[i],buffer,temp_size+4,0);
            }
        }
	}
	
    /*close the socket*/
	close(socket_fd);
}